//
// Created by martin on 8/28/21.
//

#include "LISTaction.h"
#include "../dir.h"
#include "error.h"
#include "socket.h"
#include "cmdaction.h"
#include "ef_string.h"
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <string.h>

static void data_conn_event_cb(struct bufferevent *bev, short what, void *ctx);
static void data_conn_write_cb(struct bufferevent *bev, void *ctx);
static void LIST_close(Session *sess);

#if 0
static int LIST_get_data(Session *sess, char *data, size_t size)
{
    // 准备list命令要发送的数据内容
    if (dir_listFiles(sess->dir, data, size) < 0) { // 得到指定目录的子文件列表数据（$ls -al），存入data[]
        err_msg("list failed");
        return -1;
    }

    return 0;
}

#else
static int LIST_get_data(Session *sess, struct ef_string *s)
{
    // 准备list命令要发送的数据内容
    if (dir_listFiles(sess->dir, s) < 0) { // 得到指定目录的子文件列表数据（$ls -al），存入data[]
        err_msg("list failed");
        return -1;
    }

    return 0;
}
#endif

static void data_conn_write_cb(struct bufferevent *bev, void *ctx)
{
    Session *sess = (Session *)ctx;

    switch (sess->send_data_status) {
        case DataTransferStatus_Idle:
        {
            sess->send_data_status = DataTransferStatus_Start;
            bufferevent_trigger(bev, EV_WRITE, 0);
            break;
        }

        case DataTransferStatus_Start:
        {
            struct ef_string *s = NULL;
            s = ef_string_new();
            if (!s) {
                goto errout;
            }

            if (LIST_get_data(sess, s) < 0) {
                sess->send_data_status = DataTransferStatus_Error;
                send_reply(sess->bev, 450, "Requested file action not taken.");
                return;
            }

            sess->send_data_status = DataTransferStatus_Finished;
            send_reply(sess->bev, 125, "Data connection already opened; transfer starting.");
            size_t len = ef_string_get_length(s);
            if (len == 0) {
                bufferevent_trigger(bev, EV_WRITE, 0);
            }
            else {
                bufferevent_write(sess->data_bev, ef_string_cstr(s), len);
            }

            ef_string_free(&s);
            break;
        }

        case DataTransferStatus_Finished:
        {
            send_reply(sess->bev, 226, "Transfer complete.");
            LIST_close(sess);
            break;
        }

        default:
            LIST_close(sess);
            ;
    }

    return;

errout:
    printf("LIST data_conn_write_cb errout\n");
    send_reply(bev, 451, "Requested action aborted: local error in processing.");
    LIST_close(sess);
}

static void LIST_close(Session *sess)
{
    if (sess->data_bev) {
        bufferevent_free(sess->data_bev);
        sess->data_bev = NULL;
    }

    sess->send_data_status = DataTransferStatus_Idle;
}

static void data_conn_event_cb(struct bufferevent *bev, short what, void *ctx)
{
    Session *sess = (Session *)ctx;
    struct bufferevent *ctrl_bev = (struct bufferevent *)sess->bev;

    if (what & BEV_EVENT_CONNECTED) { // success to build up data channel
        sess->send_data_status = DataTransferStatus_Start;
        bufferevent_trigger(bev, EV_WRITE, 0);
        printf("data conn: success to build up data connection\n");
    }
    else if (what & BEV_EVENT_EOF) { // data transfer done
        sess->send_data_status = DataTransferStatus_Finished;
        printf("data conn: connection finished. close data conn. \n");
        LIST_close(sess);
    }
    else if (what & BEV_EVENT_ERROR) { // error occurs to data channel
        sess->send_data_status = DataTransferStatus_Error;
        printf("data conn: connection errors. close data conn.\n");
        send_reply(ctrl_bev, 421, "Data connection timeout.");
        LIST_close(sess);
    }
    else if (what & BEV_EVENT_TIMEOUT) { // timeout to data channel
        sess->send_data_status = DataTransferStatus_Error;
        printf("data conn: connection timeout. close data conn.\n");
        send_reply(ctrl_bev, 421, "Data connection timeout.");
        LIST_close(sess);
    }
}

int LIST_action(Session *sess)
{

    LIST_close(sess);
    // connect client using PORT mode
    // client ip, port are provided by PORT command
    if (connect_PORT(sess, NULL, data_conn_write_cb, data_conn_event_cb) < 0) {
        send_reply(sess->bev, 425, "Can't open data connection.");
        return -1;
    }

#if 0
    // 数据通道传输前，应答125或150
    send_reply(sess->bev, 125, "Data connection already opened; transfer starting.");

    char data[4096];
    LIST_get_data(sess, data, sizeof(data));
    printf("data len = %d\n", len);
    printf("data = %s\n", data);

    // 数据通道传输
//    send_data(sess->data_bev, "-rw-rw-r--  1 martin martin 724314199 Jun 30 01:02 CLion-2021.1.2.tar.gz\r\n");
    send_data(sess->data_bev, data);

    // 数据通道传输完成，应答226或250
    send_reply(sess->bev, 250, "Transfer complete.");
#endif

    return 0;
}
